/* File: AttackStrategy.java
 * Purpose: Encapsulates a strategy for an organism to attack another organism
 * Author: Team Karp
 * Date: 12/11/07
 * Revisions: 12/11/07, Adam Patterson - Initial Version
 *          [2008-01-13, Adam Patterson - Implemented basic attacking]
 *          [2008-01-22, Adam Patterson - Implemented scripted attacking]
 * Additional Requirements: 
 */
package protovitae.data;

import javax.script.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * @version 1.0
 * @created 06-Dec-2007 16:19:09
 */
public class AttackStrategy extends GeneActionStrategy {

    private Invocable attackScript = null;

    /**
     * Default constructor.
     */
    public AttackStrategy() {

    }

    /**
     * This method is called to attack an opponent
     * @param information - the opponent ConcreteOrgnaism
     * @param parent - the owning ConcreteOrganism
     * @return the amount of damage done to the opponent
     */
    public Object execute(Object information, ConcreteOrganism parent) {
        ConcreteOrganism opponent = (ConcreteOrganism) information;

        //default damage value to be scripted
        //int result = parent.getAttribute(Organism.ATTRIBUTE_TYPE.BRAWN).compare(opponent.getAttribute(Organism.ATTRIBUTE_TYPE.BRAWN));

        Double damage = 0d;
        //attackScript = null; // Remove this when attack script doesn't suck.
        if (attackScript != null) {
            try {
                damage = (Double) attackScript.invokeFunction("attack", parent, opponent,this);
            } catch (NoSuchMethodException nsme) {
                nsme.printStackTrace();
                damage = 0d;
            } catch (ScriptException se) {
                se.printStackTrace();
                damage = 0d;
            } catch (Exception e) {
                e.printStackTrace();
                damage = 0d;
            }
        } else {
            damage = 0d;
        }

        if (damage < 0) {
            damage = 0d;
        }
        return new Integer(damage.intValue());
    }
    
    private Double stockDamage(ConcreteOrganism organism) {
        return (double)((organism.getAttribute(Organism.ATTRIBUTE_TYPE.BRAWN).getCurrent() * 
                organism.getAttribute(Organism.ATTRIBUTE_TYPE.BRAWN).getMax()) % 
                organism.getAttribute(Organism.ATTRIBUTE_TYPE.HEALTH).getMax());
    }

    public void initialize(ConcreteOrganism organism) {
    // Use the variables 
    }

    /**
     * @param parent
     */
    public void save(Element parentElm, Document doc) {
        Element attack = doc.createElement("attack");
        
        Element xmlcode = doc.createElement("code");
        xmlcode.appendChild(doc.createTextNode(code));
        attack.appendChild(xmlcode);
        
        //Write genetic byte array
        Element gene = doc.createElement("genedata");
        for(Byte thebyte : geneticInformation){
            Element belm = doc.createElement("b");
            gene.appendChild(doc.createTextNode(thebyte.toString()));
            gene.appendChild(belm);
        }
        attack.appendChild(gene);
        
        parentElm.appendChild(attack);
    }

    public void load(Element input) {
        try{
            NodeList atkroot = input.getElementsByTagName("attack");
            NodeList atkcode = ((Element)atkroot.item(0)).getElementsByTagName("code");
            NodeList atkdata = ((Element)atkroot.item(0)).getElementsByTagName("genedata");
            
            parse(atkcode.item(0).getTextContent()); //Calling parse will throw an exception for us if bad code. This is good.
            for(int i=0; i<atkdata.getLength(); i++){
                if(atkdata.item(i).getNodeName().equals("b"))
                   geneticInformation.add(new Byte(Byte.parseByte(atkdata.item(i).getTextContent())));
            }
        }
        catch(Exception e){
            e.printStackTrace(); //A bug occured
        }
    }
    
    /**
     * This parses the code string and stores it in param code.
     * @param code
     * @throws javax.script.ScriptException
     */
    public void parse(String code) throws ScriptException {
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

        //load the code into a function with parameters to use
        String scriptedFunctionality = "";
        scriptedFunctionality += "function attack(parent,opponent,gene) {\n";
        scriptedFunctionality += "var damage = 0;\n";
        scriptedFunctionality += code;
        scriptedFunctionality += "return damage;\n";
        scriptedFunctionality += "}";

        if(code != null && code.length() > 0){
            jsEngine.eval(scriptedFunctionality);
            attackScript = (Invocable) jsEngine;
        }
        else
            attackScript = null; //Needs to be null, otherwise we'll try and execute it and won't like the return value.
        this.code = code;
    }
}
